Springboot +JWT实现登录认证,密码加密及Token校验全过程(附源码)

您所在的位置:网站首页 spring security app认证token Springboot +JWT实现登录认证,密码加密及Token校验全过程(附源码)

Springboot +JWT实现登录认证,密码加密及Token校验全过程(附源码)

2024-07-10 06:08| 来源: 网络整理| 查看: 265

JWT实现登录认证 简介环境1. 依赖2. token生成及校验3. 登录4. 编写拦截器进行token校验5. 源码下载

简介

通俗地说,JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输。JWT的认证流程如下:

首先,前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的传输(HTTPS),从而避免敏感信息被嗅探

后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串

后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的JWT Token即可

前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)

后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等等

验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

在这里插入图片描述 本博客项目源码地址:

项目源码github地址项目源码国内gitee地址 环境

本教程使用jdk11,其他环境自行测试 在这里插入图片描述

api测试工具postman,{{localhost}}请自行更改为自己的地址,如 localhost:9999 在这里插入图片描述

1. 依赖 1.1 pom导入依赖 org.projectlombok lombok true io.springfox springfox-boot-starter 3.0.0 javax.xml.bind jaxb-api io.jsonwebtoken jjwt 0.9.1 com.auth0 java-jwt 3.18.3 2. token生成及校验 2.1 封装用户

存储用户的基本信息

import lombok.Data; import lombok.experimental.Accessors; /** * @author l */ @Data @Accessors(chain = true) public class JwtUser { private boolean valid; private String userId; private String role; public JwtUser() { this.valid = false; } } 2.2 编写JWT提供者

主要关注 createToken 和 checkToken 两个方法

createToken 生成tokencheckToken 校验token

createToken

import io.jsonwebtoken.*; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.extern.slf4j.Slf4j; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Date; /** * date: 2021-01-05 08:48 * description token管理 * * @author qiDing */ @Slf4j @ApiModel("token提供者") public class TokenProvider { @ApiModelProperty("盐") private static final String SALT_KEY = "links"; @ApiModelProperty("令牌有效期毫秒") private static final long TOKEN_VALIDITY = 86400000; @ApiModelProperty("权限密钥") private static final String AUTHORITIES_KEY = "auth"; @ApiModelProperty("Base64 密钥") private final static String SECRET_KEY = Base64.getEncoder().encodeToString(SALT_KEY.getBytes(StandardCharsets.UTF_8)); /** * 生成token * @param userId 用户id * @param clientId 用于区别客户端,如移动端,网页端,此处可根据自己业务自定义 * @param role 角色权限 */ public static String createToken(String userId, String clientId, String role) { Date validity = new Date((new Date()).getTime() + TOKEN_VALIDITY); return Jwts.builder() // 代表这个JWT的主体,即它的所有人 .setSubject(String.valueOf(userId)) // 代表这个JWT的签发主体 .setIssuer("") // 是一个时间戳,代表这个JWT的签发时间; .setIssuedAt(new Date()) // 代表这个JWT的接收对象 .setAudience(clientId) .claim("role", role) .claim("userId", userId) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .setExpiration(validity) .compact(); } /** * 校验token */ public static JwtUser checkToken(String token) { if (validateToken(token)) { Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); String audience = claims.getAudience(); String userId = claims.get("userId", String.class); String role = claims.get("role", String.class); JwtUser jwtUser = new JwtUser().setUserId(userId).setRole(role).setValid(true); log.info("===token有效{},客户端{}", jwtUser, audience); return jwtUser; } log.error("***token无效***"); return new JwtUser(); } private static boolean validateToken(String authToken) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(authToken); return true; } catch (Exception e) { log.error("无效的token:" + authToken); } return false; } } 3. 登录 3.1 密码加密

对密码进行md5加密

import org.springframework.util.DigestUtils; /** * 密码加密工具类 * * @author liangQiDing */ public class PasswordEncoder { /** * 密码加密 * @param rawPassword 登录时传入的密码 */ public static String encode(CharSequence rawPassword) { return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes()); } /** * 密码对比 * @param rawPassword 登录时传入的密码 * @param encodedPassword 数据库保存的加密过的密码 */ public static boolean matches(CharSequence rawPassword, String encodedPassword) { return encodedPassword.equals(DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes())); } } 3.2 登录接口编写 import com.example.jwt_dome.config.PasswordEncoder; import com.example.jwt_dome.jwt.AuthStorage; import com.example.jwt_dome.jwt.JwtUser; import com.example.jwt_dome.jwt.TokenProvider; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; /** * @author liangQiDing */ @RestController @Api("token测试服务器") public class TokenController { /** * 模拟数据库数据 账号 admin 密码 123456 */ private final static HashMap USER = new HashMap() { { put("admin", "e10adc3949ba59abbe56e057f20f883e"); } }; @GetMapping("/login") @ApiOperation("登陆示例(账号admin,密码123456)") public String login(String username, String password) { if (PasswordEncoder.matches(password, USER.get(username))) { // 模拟一个用户的数据 用户id为1 登录端为网页web 角色是admin return TokenProvider.createToken("1", "web", "admin"); } return "error"; } @GetMapping("/token/validate") @ApiOperation("token校验") public JwtUser tokenValidate(String token) { return TokenProvider.checkToken(token); } }

3.3 token获取测试 在这里插入图片描述

3.4 token校验测试 在这里插入图片描述

4. 编写拦截器进行token校验 4.1 存储授权信息

用于在我们授权通过后,在请求中获取用户的信息

/** * 存储本次请求的授权信息,适用于各种业务场景,包括分布式部署 * * @author lqd */ public class AuthStorage { @ApiModelProperty("请求头token的下标") public static final String TOKEN_KEY = "token"; /** * 模拟session */ private static final HashMap JWT_USER = new HashMap(); /** * 全局获取用户 */ public static JwtUser getUser() { HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); return JWT_USER.get(request.getHeader(TOKEN_KEY)); } /** * 设置用户 */ public static void setUser(String token, JwtUser user) { JWT_USER.put(token, user); } /** * 清除授权 */ public static void clearUser() { HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); JWT_USER.remove(request.getHeader(TOKEN_KEY)); } } 4.2 配置拦截器

在请求响应前,校验token,校验通过后存储用户信息。

import com.example.jwt_dome.jwt.AuthStorage; import com.example.jwt_dome.jwt.JwtUser; import com.example.jwt_dome.jwt.TokenProvider; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 拦截器 * * @author lqd */ @Slf4j public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader(AuthStorage.TOKEN_KEY); if (StringUtils.hasLength(token)) { JwtUser jwtUser = TokenProvider.checkToken(token); // 是否认证通过 if (jwtUser.isValid()) { // 保存授权信息 AuthStorage.setUser(token, jwtUser); return true; } } response.setContentType("text/html;charset=utf-8"); response.getWriter().write("请先登录!"); return false; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 请求完成清除授权信息 AuthStorage.clearUser(); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } } 4.3 配置拦截路径 import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * 配置拦截器路径 * * @author lqd */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) // 拦截的路径 .addPathPatterns("/**") // 开放的路径 .excludePathPatterns("/login/**", "/token/validate"); } } 4.4 拦截测试

在controller层添加测试接口

@GetMapping("/get/Info") @ApiOperation("模拟拦截") public String getInfo() { // 从全局环境中获取用户id JwtUser user = AuthStorage.getUser(); return "用户:"+user.getUserId() + ",请求成功"; }

普通访问 在这里插入图片描述

请求头添加token后再访问 在这里插入图片描述

5. 源码下载 Springboot开发脚手架,集合各种常用框架使用案例,完善的文档,致力于让开发者快速搭建基础环境并让应用跑起来。项目源码国内gitee地址项目源码github地址


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3